package edu.washington.ling.syntric.proc;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import edu.washington.ling.syntric.io.TreeFileReader;
import edu.washington.ling.syntric.io.TreeRepresentation;
import edu.washington.ling.syntric.struct.error.UnreadableRepresentationException;
import edu.washington.ling.syntric.struct.tree.Node;
import edu.washington.ling.syntric.struct.tree.Token;
import edu.washington.ling.syntric.struct.tree.Tree;
import edu.washington.ling.syntric.util.Utils;

public class ExtractStackingOrders {
	/*						*
	 * Stacking order stuff *
	 * 						*
	 *						*/
	private static String getStackingOrder(List<String> stacking) {
		String stackingOrder = "";
		String topLabel = "";
		boolean rightTop = false;
		boolean rightLast = false;
		boolean bothSeen = false;
		boolean cycleSeen = false;
		String level, side, label;
		String[] levelElems;
		for (int i = 0; i < stacking.size(); i++) {
			level = stacking.get(i);
			levelElems = level.split(":\\|\\|:");
			label = levelElems[0];
			side = levelElems[1];
			if (i == 0) {
				topLabel = label;
				if (side.equals("right")) {
					rightTop = true;
					rightLast = true;
				}
			}
			else {
				if (side.equals("right")) {
					if (rightLast == false && bothSeen) {
						cycleSeen = true;
					}
					if (rightLast == false) {
						bothSeen = true;
					}
					rightLast = true;
					
				}
				else if(side.equals("left")) {
					if (rightLast && bothSeen) {
						cycleSeen = true;
					}
					if (rightLast) {
						bothSeen = true;
					}
					rightLast = false;
				}
			}
		}
		if (rightTop && !bothSeen) {
			stackingOrder = "rightOnly";
		}
		else if (rightTop && bothSeen && !cycleSeen) {
			stackingOrder = "rightTop";
		}
		else if (rightTop && cycleSeen) {
			stackingOrder = "rightMixed";
		}
		else if (!rightTop && !bothSeen) {
			stackingOrder = "leftOnly";
		}
		else if (!rightTop && bothSeen && !cycleSeen) {
			stackingOrder = "leftTop";
		}
		else if (!rightTop && cycleSeen) {
			stackingOrder = "leftMixed";
		}
		return topLabel + ":||:" + stackingOrder;
	}
	private static List<String> getAdjunctStackingOrders(List<String> stacking) {
		String lastLevel = "";
		List<String> adjunctStackingOrders = new LinkedList<String>();
		List<String> adjunctStacking = new LinkedList<String>();
		for (String level: stacking) {
			
			String[] levelElems = level.split(":\\|\\|:");
			String[] lastLevelElems = lastLevel.split(":\\|\\|:");
			String label = levelElems[0];
			String lastLabel = lastLevelElems[0];
			if (label.equals(lastLabel)) {
				adjunctStacking.add(level);
			}
			else { 
				if (adjunctStacking.size() > 0) {
					//**Uncomment for verbose output**
					//for (String adjLevel: adjunctStacking) { 
					//	System.out.println(adjLevel + " hi");
					//}
					//System.out.println();
					adjunctStackingOrders.add(getStackingOrder(adjunctStacking));
					adjunctStacking = new LinkedList<String>();
				}
			}
			lastLevel = level;
		}
		if(adjunctStacking.size() > 0) {
			//**Uncomment for verbose output**
			//for (String adjLevel: adjunctStacking) {
			//	System.out.println(adjLevel);
			//}
			//System.out.println();
			adjunctStackingOrders.add(getStackingOrder(adjunctStacking));
			adjunctStacking = new LinkedList<String>();
		}
		return adjunctStackingOrders;
	}
	

	private static List<String> extractStacking(Node n, String head, String tag) {
		List<String> stacking = new LinkedList<String>();
		if (n.hasPSParent()) {
			Node parent = n.getPSParent();
			boolean headSeen = false;
			if (parent.getHeadWords().contains(head)) {
				stacking = extractStacking(parent, head, tag);
				for (Node child: parent.getPSChildren()) {
					if (!child.getHeadWords().contains(head)) {
						if(headSeen) {
							stacking.add(String.format("%s-->%s:||:right", tag, parent.getLabel()));
						} else {
							stacking.add(String.format("%s-->%s:||:left", tag, parent.getLabel()));
						}
					} else {
						headSeen = true;
					}
				}
			}
		}
		return stacking;
	}
	private static Map<String, Map<String, Integer>> extractStackingOrders() {
		Map<String, Map<String, Integer>> orderMap = new HashMap<String, Map<String, Integer>>();
		int numTrees = 0;
		try {
			TreeFileReader tfr = TreeFileReader.newReader(opts.inputFilePath, opts.inputFileFormat);
			CSTreeReader csr = CSTreeReader.newReader(opts.inputTreeFormat);
			TreeRepresentation rep = null;
			List<String> stacking;
			List<String> adjunctStackingOrders;
			while((rep = tfr.readNextTreeRep()) != null) {
				try {
					Tree cs = csr.readTree(rep);
					CheckConsistency.labelHeadWordsForTree(cs);
					for(Token t: cs.getTokenList()) {
						if (t.hasDSDependents()) {
							stacking = extractStacking(t, t.getName(), t.getTag());
							adjunctStackingOrders = getAdjunctStackingOrders(stacking);
							for (String adjunctStackingOrder: adjunctStackingOrders) {
								String[] asoElems = adjunctStackingOrder.split(":\\|\\|:");
								String label = asoElems[0];
								String order = asoElems[1];
								if (!orderMap.containsKey(label)) {
									orderMap.put(label, new HashMap<String,Integer>());
								}
								int count = 0;
								if(orderMap.get(label).containsKey(order)) {
									count = orderMap.get(label).get(order);
								}
								orderMap.get(label).put(order, count + 1);
							}
						}
					}
					numTrees++;
				}
				catch (UnreadableRepresentationException e) {
					pHandler.handle(e, "could not process representation.");
				}
			}
			System.out.println("Extraction of stacking orders was successful.");
			System.out.println(String.format("A total of %d orders were extracted from %d trees.", 
											 orderMap.size(), numTrees));
		} catch(Exception e) {
			pHandler.handle(e, "extraction of stacking orders failed.");
			e.printStackTrace();
			System.exit(1);
		}
		return orderMap;
	}
	
	private static void printStackingOrders(Map<String, Map<String,Integer>> orderMap) {
		System.out.println("Stacking orders extracted from training data:");
		LinkedList<String> orderCountStrs;
		for (String label: orderMap.keySet()) {
			orderCountStrs = new LinkedList<String>(); 
			for (String order: orderMap.get(label).keySet()) {
				orderCountStrs.add(String.format("%s(%d)", order, orderMap.get(label).get(order)));
			}
			String orderStr = Utils.join(orderCountStrs, ", ");
			System.out.println(String.format("Head-->Projection: %s, Orders: %s", label, orderStr));
		}
	}
	
}
